Lucy's Dad

Lucy's Dad

最後更新日:2023/01/09

[Service Worker]使用.Net Core快速實作瀏覽器推播(Push Notifications)功能

瀏覽器推播通知(Push Notifications)對於網站製作者來說是個很好的功能,當使用者允許通知後,無論他有沒有停留在網站上,或是沒在用手機,都可以像APP通知那樣,將更新內容傳送給使用者,進而維持黏著度,以下就讓我們來快速的製作這個功能吧。


推播通知是利用了Service Worker,也就是瀏覽器的背景來執行運作的,要完成使用者訂閱、網站端發通知、使用者接收,需要下面幾個步驟:

  1. 準備好serviceWorker.js檔案,並放在網站根目錄。
  2. 取得VPID金鑰,防止任何人都可以傳送通知給這個使用者。
  3. 準備好一個html檔案,放置一個按鈕,撰寫相關javascript,使用者按下後會詢問是否給予通知的權限。
  4. 得到使用者權限後,取得回傳的endPoint、p256dh、auth值。
  5. 使用.Net Core來發送通知給使用者。

讓我們來開始吧!

1.準備好serviceWorker.js,並放在網站根目錄。

this.addEventListener('install', function(event) {
    console.log('安裝service wroker');
});

this.addEventListener('activate', function(event){
    console.log('service wroker已啟動!')
});

this.addEventListener('fetch', function(event) {
    console.log('Handling fetch event for', event.request.url);
});

// controlling service worker
this.addEventListener("message", function(e) {
    // e.source is a client object
    e.source.postMessage("Hello! Your message was: " + e.data);
});

this.onpush = function(event) {
    console.log(event.data);
    // From here we can write the data to IndexedDB, send it to any open
    // windows, display a notification, etc.
}
this.addEventListener('push', function (e) {
    var body;

    if (e.data) {
        body = e.data.text();
    } else {
        body = "Standard Message";
    }

    var options = {
        body: body,
        icon: "/assets/img/xxxxx.jpeg",  //顯示網站LOGO  絕對或相對路徑都可
        image: '/assets/img/xxxxx.jpeg', //顯示通知的圖片  絕對或相對路徑都可
        vibrate: [100, 50, 100],
        data: {
            dateOfArrival: Date.now()
        },
        actions: [
            {
                action: "explore", title: "瀏覽最新文章",
                icon: "images/checkmark.png"  //按鈕的icon圖  絕對或相對路徑都可
            },
            {
                action: "close", title: "關閉",
                icon: "images/red_x.png"  //按鈕的icon圖  絕對或相對路徑都可
            },
        ]
    };
    e.waitUntil(
        self.registration.showNotification("露西的爹有新文章囉", options)
    );
});

this.addEventListener('notificationclick', function (e) {
    var notification = e.notification;
    var action = e.action;

    if (action === 'close') {
        notification.close();
    } else {
        // 按下瀏覽最新文章要做的動作
        clients.openWindow('https//www.lucysdad.com');
        notification.close();
    }
});
    

2.取得VPID金鑰,防止任何人都可以傳送通知給這個使用者,可直接上這個網站線上產生公鑰及私鑰,並留存等下使用。

3.準備好一個html檔案,放置一個按鈕,並設定click後要執行的function。

<button type="button" onclick="InstallServiceWorker()">

4.撰寫相關javascript,使用者按下後會詢問是否給予通知的權限。

function InstallServiceWorker() {

    if ('serviceWorker' in navigator) { //判斷瀏覽器是否支援serviceWorker
        navigator.serviceWorker
            .register('/serviceworker.js') // 註冊 Service Worker 路徑為第一步驟的serviceworker.js
            .then(function (reg) {
                //console.log('Registration succeeded. Scope is ' + reg.scope); // 註冊成功  取消註解後會印出service worker可使用的範圍
                //註冊後判斷使用者是否之前已經訂閱過了
                if (Notification.permission === "granted") {
                    console.log("已經訂閱過了");
                    alert("您已經訂閱過囉");
                    getSubscription(reg);
                } else if (Notification.permission === "blocked") {
                    console.log("之前拒絕了訂閱"); //可跳訊息視窗告知如何重新訂閱
                } else {
                    console.log("還沒訂閱過"); 
                    requestNotificationAccess(reg); //顯示訂閱視窗
                }

            })
            .catch(function (error) {
                console.log('Registration failed with ' + error); // 註冊失敗
            });
    } else { //瀏覽器不支援serviceWorker 可能是版本太舊
        alert("很抱歉,您的瀏覽器不支援訂閱的功能,請升級您的瀏覽器。");
    }
}
function requestNotificationAccess(reg) {
    Notification.requestPermission(function (status) {
        if (status == "granted") {
            //console.log("使用者按下訂閱了");
            var options = {
                body: '您已成功訂閱露西的爹!',
                icon: '/assets/img/_____.jpg'  //顯示網站LOGO  絕對或相對路徑都可
            }
            new Notification('訂閱完成!!', options);  //即時發送一個通知,告訴使用者已完成訂閱
            getSubscription(reg);
        } else {
            //console.log("使用者拒絕了訂閱");
        }
    });
}
function getSubscription(reg) {
    reg.pushManager.getSubscription().then(function (sub) {
        if (sub === null) {
            reg.pushManager.subscribe({
                userVisibleOnly: true,
                applicationServerKey: "BAEhJJK8pqg33cGF4pWcY.......HXNjUPtteNHcCf01jBVWY7018oQm9zQ"   //這是步驟2所取得到的 public Key
            }).then(function (sub) {
                fillSubscribeFields(sub);
            }).catch(function (e) {
                console.error("Unable to subscribe to push", e);
            });
        } else {
            fillSubscribeFields(sub);
        }
    });
}


function fillSubscribeFields(sub) {
    //步驟五所提到的 得到使用者權限後,取得回傳的endPoint、p256dh、auth值
    //可自行將這三個值透過ajax存入您的資料庫
    console.log("sub.endpoint:" + sub.endpoint); //endPoint為要傳送通知時,每個人都有不同的api網址
    console.log("p256dh:" + arrayBufferToBase64(sub.getKey("p256dh")));
    console.log("auth:" + arrayBufferToBase64(sub.getKey("auth")));
    
}

function arrayBufferToBase64(buffer) {
    var binary = '';
    var bytes = new Uint8Array(buffer);
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return window.btoa(binary);
}
    

5.得到使用者權限後,取得回傳的endPoint、p256dh、auth值,這部分已在上一步驟的fillSubscribeFields function裡完成。

6.使用.Net Core來發送通知給使用者,請先安裝WebPush-NetCore套件,接者撰寫以下程式。

var subject = "mailto:XXXXX@gmail.com";  //申請VAPID所輸入的email
var publicKey = "BAHWfhher623h....cfhFjFUjNKs";  //步驟2所取得到的 public Key
var privateKey = "privateKey-privateKey-privateKey-privateKey";  //步驟2所取得到的 private Key
var vapidDetails = new VapidDetails(subject, publicKey, privateKey);
PushSubscription subscription = new PushSubscription(
    "https://fcm.googleapis.com/fcm/send/......",   //步驟5得到的endPoint值
    "BAct3KDe0CyW6Cc1De0CyW6CPxy.......De0CyW6C",  //步驟5得到的p256dh值
    "HRIePb8....UlftQ==");  //步驟5得到的auth值
var webPushClient = new WebPushClient();
try
{
    webPushClient.SendNotification(subscription, "[居家風水]告別2022!2023年九宮飛星開運風水佈局", vapidDetails);
}
catch (Exception)
{
}

搞定收工,使用者收到的通知樣式可以在步驟一的this.addEventListener('push', function (e) {}監聽事件裡面完成。

本篇只是快速的帶各位完成使用者訂閱及網站端發送通知的功能,其中service worker的生命週期、使用範圍、監聽事件等等,有很多原理可以自行去研究,精進自己的程式能力。

參考網站:
https://blog.elmah.io/how-to-send-push-notifications-to-a-browser-in-asp-net-core/
https://ithelp.ithome.com.tw/users/20117813/ironman/2219
https://www.cythilya.tw/2018/02/17/firebase-push-notification/

0 則留言